package cheme.ProcessEditor.Parts.Streams;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.draw2d.AbsoluteBendpoint;
import org.eclipse.draw2d.AutomaticRouter;
import org.eclipse.draw2d.Bendpoint;
import org.eclipse.draw2d.BendpointLocator;
import org.eclipse.draw2d.Graphics;
import org.eclipse.draw2d.Label;
import org.eclipse.draw2d.Locator;
import org.eclipse.draw2d.MidpointLocator;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.PointList;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.gef.ConnectionEditPart;
import org.eclipse.gef.DragTracker;
import org.eclipse.gef.Request;
import org.eclipse.gef.RequestConstants;
import org.eclipse.gef.SharedCursors;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.editpolicies.BendpointEditPolicy;
import org.eclipse.gef.editpolicies.SelectionHandlesEditPolicy;
import org.eclipse.gef.handles.BendpointCreationHandle;
import org.eclipse.gef.handles.BendpointHandle;
import org.eclipse.gef.handles.BendpointMoveHandle;
import org.eclipse.gef.requests.BendpointRequest;
import org.eclipse.gef.tools.ConnectionBendpointTracker;
import org.eclipse.swt.SWT;

import cheme.ProcessModel.Cmds.CreateStreamBendpointCmd;
import cheme.ProcessModel.Cmds.DeleteStreamBendpointCmd;
import cheme.ProcessModel.Cmds.MoveStreamBendpointCmd;
import cheme.ProcessModel.ProcessUnits.Streams.IStream;


public class ChemeBendpointPolicy extends BendpointEditPolicy
{
	private int handlesize = 10;
	private static final List NULL_CONSTRAINT = new ArrayList();
	
	@Override
	public Command getCommand(Request request)
	{
		//System.out.println("ChemeBendpointPolicy " + request.getType());
		
		return super.getCommand(request);
	}
	
	protected Command getCreateBendpointCommand(BendpointRequest request)
	{
		Point point = request.getLocation();
		getConnection().translateToRelative(point);

		CreateStreamBendpointCmd command = new CreateStreamBendpointCmd();
		command.setLocation(point);
		command.setStream((IStream)getHost().getModel());
		command.setIndex(request.getIndex());

		return command;
	}

	protected Command getDeleteBendpointCommand(BendpointRequest request)
	{
		DeleteStreamBendpointCmd command = new DeleteStreamBendpointCmd();
		command.setStream((IStream)getHost().getModel());
		command.setIndex(request.getIndex());
		return command;
	}

	protected Command getMoveBendpointCommand(BendpointRequest request)
	{
		Point location = request.getLocation();
		getConnection().translateToRelative(location);

		MoveStreamBendpointCmd command = new MoveStreamBendpointCmd();
		command.setStream((IStream)getHost().getModel());
		command.setIndex(request.getIndex());
		command.setNewLocation(location);

		return command;
	}
	
	protected List createSelectionHandles()
	{
		List list = new ArrayList();
		if (isAutomaticallyBending())
		{
			list = createChemeHandlesForAutomaticBendpoints();
		}
		else
		{
			list = createChemeHandlesForUserBendpoints();
		}
	 	return list;
	}
	
	private boolean isAutomaticallyBending()
	{
		List constraint = (List)getConnection().getRoutingConstraint();
		PointList points = getConnection().getPoints();
		return ((points.size() > 2) && (constraint == null || constraint.isEmpty()));
	}
	
	private List createChemeHandlesForAutomaticBendpoints()
	{
		List list = new ArrayList();
		ConnectionEditPart connEP = (ConnectionEditPart)getHost();
		PointList points = getConnection().getPoints();
		
		for (int i = 0; i < points.size() - 1; i++)
		{
			list.add(new ChemeBendpointCreationHandle(connEP, 0, i));
		}
		
		return list;
	}

	private List createChemeHandlesForUserBendpoints()
	{
		List list = new ArrayList();
		ConnectionEditPart connEP = (ConnectionEditPart)getHost();
		PointList points = getConnection().getPoints();
		List bendPoints = (List)getConnection().getRoutingConstraint();
		int bendPointIndex = 0;
		Point currBendPoint = null;
		
		if (bendPoints == null)
		{
			bendPoints = NULL_CONSTRAINT;
		}
		else if (!bendPoints.isEmpty())
		{
			currBendPoint = ((Bendpoint)bendPoints.get(0)).getLocation();
		}
		
		for (int i = 0; i < points.size() - 1; i++)
		{
			//Put a create handle on the middle of every segment
			list.add(new ChemeBendpointCreationHandle(connEP, bendPointIndex, i));
			
			//If the current user bendpoint matches a bend location, show a move handle
			if (i < points.size() - 1 && bendPointIndex < bendPoints.size() && currBendPoint.equals(points.getPoint(i + 1)))
			{
				list.add(new ChemeBendpointMoveHandle(connEP, bendPointIndex, i + 1));
				
				//Go to the next user bendpoint
				bendPointIndex++;
				if (bendPointIndex < bendPoints.size())
				{
					currBendPoint = ((Bendpoint)bendPoints.get(bendPointIndex)).getLocation();
				}
			}
		}

		return list;
	}
	
	public class ChemeBendpointHandle extends BendpointHandle
	{
		/**
		 * Draws the handle with fill color and outline color dependent 
		 * on the primary selection status of the owner editpart.
		 *
		 * @param g The graphics used to paint the figure.
		 */
		public void paintFigure(Graphics graphics)
		{
			graphics.pushState();
			
			graphics.setAntialias(SWT.ON);
			
			Rectangle r = getBounds();
			r.shrink(1, 1);
			try 
			{
				graphics.setBackgroundColor(getFillColor());
				graphics.fillOval(r.x, r.y, r.width, r.height);
				graphics.setForegroundColor(getBorderColor()); 
				graphics.drawOval(r.x, r.y, r.width, r.height);
			}
			finally
			{
				//We don't really own rect 'r', so fix it.
				r.expand(1, 1);
			}
			
			graphics.popState();
		}
	}
	
	/**
	 * A BendpointHandle that is used to create a new bendpoint.
	 */
	public class ChemeBendpointCreationHandle extends ChemeBendpointHandle
	{
		{
			setCursor(SharedCursors.SIZEALL);
			setPreferredSize(new Dimension(handlesize - 2, handlesize - 2));
		}

		/**
		 * Creates a new BendpointCreationHandle.
		 */
		public ChemeBendpointCreationHandle()
		{
			setToolTip(new Label("Drag this point to reorient the stream tag."));
		}

		/**
		 * Creates a new BendpointCreationHandle, sets its owner to <code>owner</code>
		 * and its index to <code>index</code>, and sets its locator to a new
		 * {@link MidpointLocator}.
		 * @param owner the ConnectionEditPart owner
		 * @param index the index
		 */
		public ChemeBendpointCreationHandle(ConnectionEditPart owner, int index)
		{
			setOwner(owner);
			setIndex(index);
			setLocator(new MidpointLocator(getConnection(), index));
		}

		/**
		 * Creates a new BendpointCreationHandle, sets its owner to <code>owner</code>
		 * and its index to <code>index</code>, and sets its locator to a new
		 * {@link MidpointLocator} with the given <code>locatorIndex</code>.
		 * @param owner the ConnectionEditPart owner
		 * @param index the index
		 * @param locatorIndex the locator index
		 */
		public ChemeBendpointCreationHandle(ConnectionEditPart owner, int index, int locatorIndex)
		{
			setOwner(owner);
			setIndex(index);
			setLocator(new MidpointLocator(getConnection(), locatorIndex));
		}

		/**
		 * Creates a new BendpointCreationHandle and sets its owner to 
		 * <code>owner</code>, sets its index to <code>index</code>, and
		 * sets its locator to <code>locator</code>.
		 * @param owner the ConnectionEditPart owner
		 * @param index the index
		 * @param locator the Locator
		 */
		public ChemeBendpointCreationHandle(ConnectionEditPart owner, int index, Locator locator)
		{
			setOwner(owner);
			setIndex(index);
			setLocator(locator);
		}

		/**
		 * Creates and returns a new {@link ConnectionBendpointTracker}.
		 * @return the new ConnectionBendpointTracker
		 */
		protected DragTracker createDragTracker()
		{
			ConnectionBendpointTracker tracker;
			tracker = new ConnectionBendpointTracker((ConnectionEditPart)getOwner(), getIndex());
			tracker.setType(RequestConstants.REQ_CREATE_BENDPOINT);
			tracker.setDefaultCursor(getCursor());
			return tracker;
		}
		

	}

	/**
	 * A BendpointHandle that is used to move an existing bendpoint.
	 */
	public class ChemeBendpointMoveHandle extends ChemeBendpointHandle
	{
		{
			setCursor(SharedCursors.SIZEALL);
			setPreferredSize(new Dimension(handlesize, handlesize));
		}

		/**
		 * Creates a new BendpointMoveHandle.
		 */
		public ChemeBendpointMoveHandle()
		{
		}

		/**
		 * Creates a new BendpointMoveHandle, sets its owner to <code>owner</code>
		 * and its index to <code>index</code>, and sets its locator to a new
		 * {@link BendpointLocator}.
		 * @param owner the ConnectionEditPart owner
		 * @param index the index
		 */
		public ChemeBendpointMoveHandle(ConnectionEditPart owner, int index)
		{
			setOwner(owner);
			setIndex(index);
			setLocator(new BendpointLocator(getConnection(), index + 1));
		}

		/**
		 * Creates a new BendpointMoveHandle, sets its owner to <code>owner</code>
		 * and its index to <code>index</code>, and sets its locator to a new
		 * {@link BendpointLocator} with the given <code>locatorIndex</code>.
		 * @param owner the ConnectionEditPart owner
		 * @param index the index
		 * @param locatorIndex the index to use for the locator
		 */
		public ChemeBendpointMoveHandle(ConnectionEditPart owner, int index, int locatorIndex)
		{
			setOwner(owner);
			setIndex(index);
			setLocator(new BendpointLocator(getConnection(), locatorIndex));
		}

		/**
		 * Creates a new BendpointMoveHandle and sets its owner to <code>owner</code>,
		 * sets its index to <code>index</code>, and sets its locator to 
		 * <code>locator</code>.
		 * @param owner the ConnectionEditPart owner
		 * @param index the index
		 * @param locator the Locator
		 */
		public ChemeBendpointMoveHandle(ConnectionEditPart owner, int index, Locator locator)
		{
			setOwner(owner);
			setIndex(index);
			setLocator(locator);
		}

		/**
		 * Creates and returns a new {@link ConnectionBendpointTracker}.
		 * @return the new ConnectionBendpointTracker
		 */
		protected DragTracker createDragTracker()
		{
			ConnectionBendpointTracker tracker;
			tracker = new ConnectionBendpointTracker((ConnectionEditPart)getOwner(), getIndex());
			tracker.setType(RequestConstants.REQ_MOVE_BENDPOINT);
			tracker.setDefaultCursor(getCursor());
			return tracker;
		}
	}
}	
	
